package cz.semjob.learning.relation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import cz.semjob.common.attribute.value.NominalStringAttributeValue;
import cz.semjob.document.AnnotatedDocument;
import cz.semjob.document.annotation.LabeledRelationWordsPair;
import cz.semjob.document.annotation.RelationWordsPair;
import cz.semjob.document.annotation.metadata.AnnotationTypeFactory;
import cz.semjob.document.annotation.metadata.Relation;
import cz.semjob.document.processing.DocumentInstance;
import cz.semjob.learning.data.TaxonomyLearningException;
import cz.semjob.learning.data.attribute.AInstanceAttribute;
import cz.semjob.learning.data.attribute.YesNoNominalInstanceAttribute;
import cz.semjob.learning.instances.LabeledRelationInstance;
import cz.semjob.learning.instances.LabeledTextInstance;
import cz.semjob.learning.instances.UnlabeledRelationInstance;
import cz.semjob.learning.instances.creator.BaseTextInstanceCreator;

@Component("baseRelationInstanceCreator")
public class BaseRelationInstanceCreator implements IRelationInstanceCreator {

	/**
	 * 
	 */
	private static final long serialVersionUID = -918383496970047328L;

	private static Logger logger = Logger
			.getLogger(BaseRelationInstanceCreator.class);

	protected BaseTextInstanceCreator textInstanceCreator;

	protected AnnotationTypeFactory annotationTypeFactory;

	/**
	 * List of current attribute names used for instance creation.
	 */
	protected List<String> attributesNamesList;

	/**
	 * List of all possible attributes that can be used for instance creation.
	 */
	protected Map<String, AInstanceAttribute> attributesMap;

	/**
	 * List of current attributes used for instance creation.
	 */
	protected List<AInstanceAttribute> attributesList;

	private final String expression1 = "Expression1";
	private final String expression2 = "Expression2";

	private static final String APPLIES_TO_SAME_ENTITIES = "SameEntities";
	protected YesNoNominalInstanceAttribute appliesToSameEntitiesAttribute = new YesNoNominalInstanceAttribute(
			APPLIES_TO_SAME_ENTITIES);

	@Autowired
	public BaseRelationInstanceCreator(
			@Qualifier("basetextinstancecreator") BaseTextInstanceCreator textInstanceCreator,
			AnnotationTypeFactory annotationTypeFactory) {
		super();
		this.textInstanceCreator = textInstanceCreator;
		this.annotationTypeFactory = annotationTypeFactory;
		this.attributesNamesList = new ArrayList<String>();
		this.attributesList = new ArrayList<AInstanceAttribute>();
		this.attributesMap = new HashMap<String, AInstanceAttribute>();

		attributesNamesList.add(APPLIES_TO_SAME_ENTITIES);

	}

	public List<LabeledRelationInstance> createRelationLabeledInstances(
			AnnotatedDocument annotatedDocument, Relation relation)
			throws TaxonomyLearningException {
		if (attributesList.isEmpty()) {
			initTextInstanceCreator();
		}
		List<LabeledRelationInstance> relationInstances = new ArrayList<LabeledRelationInstance>();
		if (annotatedDocument.getRelationsMap().containsKey(relation)) {
			List<LabeledRelationWordsPair> relationWords = annotatedDocument
					.getRelationsMap().get(relation);
			for (RelationWordsPair relationWordsPair : relationWords) {
				LabeledRelationInstance labeledInstance = createOneLabeledInstance(
						annotatedDocument, relation, relationWordsPair);
				relationInstances.add(labeledInstance);
			}

		}
		return relationInstances;
	}

	private LabeledRelationInstance createOneLabeledInstance(
			AnnotatedDocument annotatedDocument, Relation relation,
			RelationWordsPair relationWordsPair)
			throws TaxonomyLearningException {
		LabeledRelationInstance labeledInstance = new LabeledRelationInstance(
				attributesList, annotatedDocument.getDocumentInstance());
		labeledInstance.setLabel(relation);
		fillRelationInstanceAttributes(annotatedDocument.getDocumentInstance(),
				relationWordsPair, labeledInstance);

		// other attributes

		return labeledInstance;
	}

	public List<UnlabeledRelationInstance> createRelationUnlabeledInstances(
			DocumentInstance documentInstance,
			List<RelationWordsPair> relationWords)
			throws TaxonomyLearningException {
		if (attributesList.isEmpty()) {
			initTextInstanceCreator();
		}
		List<UnlabeledRelationInstance> relationInstances = new ArrayList<UnlabeledRelationInstance>();
		for (RelationWordsPair relationWordsPair : relationWords) {
			UnlabeledRelationInstance unlabeledInstance = createOneUnlabeledRelationInstance(
					documentInstance, relationWordsPair);
			relationInstances.add(unlabeledInstance);
		}
		return relationInstances;

	}

	private UnlabeledRelationInstance createOneUnlabeledRelationInstance(
			DocumentInstance documentInstance,
			RelationWordsPair relationWordsPair)
			throws TaxonomyLearningException {
		UnlabeledRelationInstance unlabeledInstance = new UnlabeledRelationInstance(
				attributesList, documentInstance);
		fillRelationInstanceAttributes(documentInstance, relationWordsPair,
				unlabeledInstance);
		return unlabeledInstance;
	}

	protected void fillRelationInstanceAttributes(
			DocumentInstance documentInstance,
			RelationWordsPair relationWordsPair,
			UnlabeledRelationInstance unlabeledInstance)
			throws TaxonomyLearningException {
		LabeledTextInstance relationText1 = textInstanceCreator
				.createOneLabeledInstance(documentInstance,
						relationWordsPair.getEntity1(), true,
						relationWordsPair.getRelationWord1());
		LabeledTextInstance relationText2 = textInstanceCreator
				.createOneLabeledInstance(documentInstance,
						relationWordsPair.getEntity2(), true,
						relationWordsPair.getRelationWord2());
		unlabeledInstance.setExpressions(relationText1, relationText2);

		if (attributesNamesList.contains(APPLIES_TO_SAME_ENTITIES)) {
			if (relationText1.getLabel().equals(relationText2.getLabel())) {
				unlabeledInstance.addAttributeValue(APPLIES_TO_SAME_ENTITIES,
						new NominalStringAttributeValue(
								APPLIES_TO_SAME_ENTITIES, "yes",
								appliesToSameEntitiesAttribute));
			} else {
				unlabeledInstance.addAttributeValue(APPLIES_TO_SAME_ENTITIES,
						new NominalStringAttributeValue(
								APPLIES_TO_SAME_ENTITIES, "no",
								appliesToSameEntitiesAttribute));
			}
		}
	}

	protected void initTextInstanceCreator() {
		attributesMap.put(APPLIES_TO_SAME_ENTITIES,
				appliesToSameEntitiesAttribute);

		this.textInstanceCreator.initTextInstanceCreator();

		for (AInstanceAttribute attribute : this.textInstanceCreator
				.getAttributesList()) {
			String name = expression1 + attribute.getName();
			AInstanceAttribute newAttribute = attribute.copyWithName(name);
			this.attributesNamesList.add(name);
			this.attributesMap.put(name, newAttribute);
		}

		for (AInstanceAttribute attribute : this.textInstanceCreator
				.getAttributesList()) {
			String name = expression2 + attribute.getName();
			AInstanceAttribute newAttribute = attribute.copyWithName(name);
			this.attributesNamesList.add(name);
			this.attributesMap.put(name, newAttribute);
		}

		for (String name : this.attributesNamesList) {
			if (attributesMap.get(name) != null) {
				AInstanceAttribute atribute = attributesMap.get(name);
				this.attributesList.add(attributesMap.get(name));
			} else {
				logger.warn("Attribute " + name
						+ " not found in the relation attributesMap");
			}
		}
	}

	public List<AInstanceAttribute> getListOfAttributes() {
		return attributesList;
	}

	public List<String> getAttributesNamesList() {
		return attributesNamesList;
	}

	public String getName() {
		return "BaseRelationInstanceCreator";
	}

	protected void updateAttributes(List<String> names) {
		this.attributesList.clear();
		for (String name : names) {
			if (attributesMap.get(name) != null) {
				this.attributesList.add(attributesMap.get(name));
			}
		}
	}

}
